package com.saicyc.demo.utils;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Stream;

@Slf4j
public class CreateCodeUtils {

    private String packageStr;
    public CreateCodeUtils(){
        String s = this.getClass().getPackage().getName();
        packageStr = s.substring(0,s.lastIndexOf("."));
    }

    public static void main(String[] args) throws Exception {

        CreateCodeUtils createCodeUtils = new CreateCodeUtils();
        System.out.println("请输入tableInfo:");
        Scanner tableInfo = new Scanner(System.in);
        String tableName = tableInfo.next();
        String tableDesc = tableInfo.next();

        //表名
        CodeTable table = new CodeTable();
        table.setTableName(tableName.replaceAll("[0-9]+\\.",""));
        table.setTableNameDesc(tableDesc.trim());
        System.out.println("请输入colinfo:");
        Scanner colInfo = new Scanner(System.in);
        List<CodeCol> codeColList = new ArrayList<>();
        while(true){
            String info = colInfo.nextLine();
            if("".equals(info)){continue;}
            if("EOF".equals(info)){
                colInfo.close();
                break;
            }
            List<String> infos = Arrays.asList(info.split("\t"));
            CodeCol codeCol = new CodeCol();
            codeCol.setColName(infos.get(0).trim());
            codeCol.setColType(infos.get(1).trim());
            codeCol.setColDesc(infos.size()>2?infos.get(2).trim():"");
            codeColList.add(codeCol);
        }

        String domainStr = createCodeUtils.createDomainModel(table,codeColList);
        String sqlStr = createCodeUtils.createSql(table,codeColList);
        String daoStr = createCodeUtils.createDao(table);
        String repositoryStr = createCodeUtils.createRepository(table);
        String repositoryImplStr = createCodeUtils.createRepositoryImpl(table);
        String voStr = createCodeUtils.createVo(table,codeColList);

        createCodeUtils.createFile(table,domainStr,"","domain",false);
        createCodeUtils.createFile(table,sqlStr,"","db/migration",true);
        createCodeUtils.createFile(table,daoStr,"Dao","dao",false);
        createCodeUtils.createFile(table,repositoryStr,"Repository","repository",false);
        createCodeUtils.createFile(table,repositoryImplStr,"RepositoryImpl","repository/impl",false);
        createCodeUtils.createFile(table,voStr,"Vo","vo",false);
    }

    private  void createFile( CodeTable table,String domainStr,String fileEnd,String path,Boolean isResource) throws IOException {
        //创建domain
        URL resource = Thread.currentThread().getContextClassLoader().getResource("");
        File file = null;
        if(!isResource) {
            String resourceUrl = resource.getPath() + "../../src/main/java/";
            String filePrefix = resourceUrl + this.getClass().getPackage().getName().substring(0, this.getClass().getPackage().getName().lastIndexOf(".")).replaceAll("\\.", "/") + "/"+path+"/";
            File mkdirs = new File(filePrefix);
            System.out.println(mkdirs.getAbsolutePath());
            mkdirs.mkdirs();
            file = new File(filePrefix + getHumpStr(table.getTableName().substring(table.getTableName().indexOf("_") + 1),1)+fileEnd + ".java");
            file.createNewFile();
        }else {
            String resourceUrl = resource.getPath()+"../../src/main/resources/";
            String filePrefix = resourceUrl;
            File mkdirs = new File(filePrefix  + path);
            System.out.println(mkdirs.getAbsolutePath());
            mkdirs.mkdirs();
            file = new File(filePrefix +path+"/"+ "V"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())+"__create_table_"+table.getTableName()+".sql");
            file.createNewFile();
        }
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(domainStr.getBytes());
        }catch (Exception e){
            log.error("",e);
        }finally {
            fileOutputStream.flush();
            fileOutputStream.close();
        }
    }

    private String createVo(CodeTable table, List<CodeCol> codeColList) throws Exception {
        String voStr = tianPackage(VoModel.str,".vo");
        voStr = tianTableDomain(voStr,table);
        voStr = tianCols(voStr,codeColList,2);
        return voStr;
    }

    private String createRepositoryImpl(CodeTable table) {
        String repositoryImplStr = tianPackage(RepositoryImplModel.str,".repository.impl");
        repositoryImplStr = tianTableDomain(repositoryImplStr,table);
        repositoryImplStr = tianRepositoryPackage(repositoryImplStr);
        repositoryImplStr = tianDaoPackage(repositoryImplStr);
        return repositoryImplStr;
    }

    private String tianDaoPackage(String repositoryImplStr) {
        String domainStr = packageStr+".dao.";
        return repositoryImplStr.replaceAll("<daoPackage>",domainStr);
    }

    private String createRepository(CodeTable table) {
        String repositoryStr = tianPackage(RepositoryModel.str,".repository");
        repositoryStr = tianTableDomain(repositoryStr,table);
        return repositoryStr;
    }

    private String createDao(CodeTable table) {
        String daoStr = tianPackage(DaoModel.str,".dao");
        daoStr = tianDomainPackage(daoStr);
        daoStr = tianTableDomain(daoStr,table);
        return daoStr;
    }

    private String tianDomainPackage(String daoStr) {
        String domainStr = packageStr+".domain.";
        return daoStr.replaceAll("<domainPackage>",domainStr);
    }

    private String tianRepositoryPackage(String daoStr) {
        String domainStr = packageStr+".repository.";
        return daoStr.replaceAll("<repositoryPackage>",domainStr);
    }

    private String createSql(CodeTable table, List<CodeCol> codeColList) {
        String sqlStr = tianTableName(SqlModel.sql,table);
        sqlStr = tianTableComment(sqlStr,table);
        sqlStr = tianPK(sqlStr,codeColList);
        sqlStr = tianSqlCol(sqlStr,codeColList);
        return sqlStr;
    }

    private String tianSqlCol(String sqlStr, List<CodeCol> codeColList) {

        List<String> sqlColList = new ArrayList<>();
        for(CodeCol codeCol:codeColList){
            String colName = "    `"+codeCol.getColName()+"`";
            String colType;
            String colDefult ="";
            if(codeCol.getColType().toLowerCase().contains("varchar")||codeCol.getColType().toLowerCase().contains("string")){
                String[] type = codeCol.getColType().split(":");
                colType = "varchar "+"("+(type.length>1?type[1]:"255")+")";
                colDefult = " NOT NULL DEFAULT '' ";
            }else if(codeCol.getColType().toLowerCase().contains("int")||"integer".equals(codeCol.getColType().toLowerCase())){
                String[] type = codeCol.getColType().split(":");
                colType = " int " +"("+(type.length>1?type[1]:"11")+")";
                colDefult = " NOT NULL DEFAULT 0 ";
            }else if(codeCol.getColType().toLowerCase().contains("bigint")||"bigint".equals(codeCol.getColType().toLowerCase())||"long".equals(codeCol.getColType().toLowerCase())||"fk".equals(codeCol.getColType().toLowerCase())){
                String[] type = codeCol.getColType().split(":");
                colType = " bigint "+ "("+(type.length>1?type[1]:"20")+")";
                colDefult = " NOT NULL DEFAULT 0 ";
            }else if(codeCol.getColType().toLowerCase().contains("boolean")){
                colType = "tinyint (4)";
                colDefult = " NOT NULL DEFAULT 0 ";
            }else if(codeCol.getColType().toLowerCase().equals("pk")){
                colType = " bigint (20)";
                colDefult = " NOT NULL AUTO_INCREMENT ";
            }else if(codeCol.getColType().toLowerCase().equals("text")){
                colType = "varchar (255)";
                colDefult = " NOT NULL DEFAULT '' ";
            }else if(codeCol.getColType().toLowerCase().equals("date")||codeCol.getColType().toLowerCase().equals("datetime")){
                colType = "datetime";
                colDefult = " NOT NULL";
            }else {
                colType = " "+codeCol.getColType()+" ";
            }

            String colDesc = " COMMENT '"+codeCol.getColDesc()+"'";
            sqlColList.add(colName+colType+colDefult+colDesc+",");
        }

        return sqlStr.replaceAll("<cols>",StringUtils.joinWith("\n",sqlColList.toArray()));
    }

    private String tianPK(String sqlStr, List<CodeCol> codeColList) {
        for(CodeCol codeCol:codeColList){
            if("pk".equals(codeCol.getColType().toLowerCase())){
                sqlStr = sqlStr.replaceAll("<PK>",codeCol.getColName());
            }
        }
        return sqlStr;
    }

    private String tianTableComment(String sqlStr, CodeTable table) {
        return sqlStr.replaceAll("<tableComment>",table.getTableNameDesc());
    }


    private  String createDomainModel(CodeTable table, List<CodeCol> codeColList) throws Exception {
        String domainStr = tianPackage(DomainModel.domainStr,".domain");
        domainStr = tianTableName(domainStr,table);
        domainStr = tianTableDomain(domainStr,table);
        domainStr = tianCol(domainStr,codeColList);
        //type 1 domain
        domainStr = tianCols(domainStr,codeColList,1);
        return domainStr;
    }

    private String tianCols(String domainStr, List<CodeCol> codeColList,Integer type) throws Exception {
        List<String> colList = new ArrayList<>();
        if(type == 1){
            for(CodeCol codeCol :codeColList){
                String id ="";
                if("pk".equals(codeCol.getColType().toLowerCase())) {
                    id = "@Id\n";
                }
                String anotation = "    @Column(name = \"<col>\") \n".replaceAll("<col>",codeCol.getColName());
                String date = "";
                if("date".equals(codeCol.getColType().toLowerCase())){
                    date = "    @Convert(converter = DateToStringConverter.class)\n";
                }
                String col ="    private "+ colTypeZhuan(codeCol.getColType())+" "+getHumpStr(codeCol.getColName(),0) + " = "+defaultValue(codeCol.getColType())+";\n";
                colList.add(id+anotation+date+col);
            }
        }else if(type == 2){
            for(CodeCol codeCol :codeColList) {
                String anotation = "    @ApiModelProperty(value = \""+(StringUtils.isNotBlank(codeCol.getColDesc())?codeCol.getColDesc():codeCol.getColName())+"\")\n";
                String colType = "    private "+ colTypeZhuan(codeCol.getColType())+" "+getHumpStr(codeCol.getColName(),0) + " = "+defaultValue(codeCol.getColType())+";\n";
                colList.add(anotation+colType);
            }
        }
        return domainStr.replaceAll("<cols>",StringUtils.joinWith("\n",colList.toArray()));
    }

    private String defaultValue(String colType) throws Exception {
        if(colType.toLowerCase().contains("varchar")||colType.toLowerCase().equals("text")||colType.toLowerCase().contains("string")){
            return "\"\"";
        }else if("bigint".equals(colType.toLowerCase())||"pk".equals(colType.toLowerCase())||"fk".equals(colType.toLowerCase())||"long".equals(colType.toLowerCase())){
            return "0L";
        }else if("int".equals(colType.toLowerCase())||"integer".equals(colType.toLowerCase())){
            return "0";
        }else if("boolean".equals(colType.toLowerCase())){
            return "false";
        }else if("date".equals(colType.toLowerCase())||"datetime".equals(colType.toLowerCase())){
            return "\"\"";
        }else{
            throw new Exception("暂不支持");
        }
    }

    private String colTypeZhuan(String colType) throws Exception {
        if(colType.toLowerCase().contains("varchar")||colType.toLowerCase().equals("text")||colType.toLowerCase().contains("string")){
            return "String";
        }else if("bigint".equals(colType.toLowerCase().trim())||
                "pk".equals(colType.toLowerCase().trim())||
                "fk".equals(colType.toLowerCase().trim())||
                "long".equals(colType.toLowerCase().trim())
        ){
            return "Long";
        }else if("int".equals(colType.toLowerCase().trim())||"integer".equals(colType.toLowerCase().trim())){
            return "int";
        }else if("boolean".equals(colType.toLowerCase().trim())){
            return "Boolean";
        }else if("date".equals(colType.toLowerCase().trim())||"datetime".equals(colType.toLowerCase().trim())){
            return "String";
        }else{
            throw new Exception("暂不支持");
        }
    }

    private String tianCol(String domainStr, List<CodeCol> codeColList) {
        String col = StringUtils.joinWith(",", codeColList.stream().map(CodeCol::getColName).toArray());
        return domainStr.replaceAll("<col>",col);
    }

    private String tianTableDomain(String domainStr, CodeTable table) {
        return domainStr.replaceAll("<tableDomain>",getHumpStr(table.getTableName().substring(table.getTableName().indexOf("_")+1),1));
    }

    public  String getHumpStr(String source,int i) {
        if (!StringUtils.isEmpty(source)) {
            if (source.contains("_")) {
                String domainStr = "";
                for(String str:source.split("_")) {
                    if(i != 0) {
                        domainStr += firstUpperCase(str);
                    }else{
                        domainStr +=str;
                        i++;
                    }
                }
                return domainStr;
            }else{
                if(i != 0){
                    return firstUpperCase(source);
                }else {
                    return source;
                }
            }
        } else {
            return "undefined";
        }
    }

    /**
     * 首字母大写转换
     *
     * @param field
     * @return
     */
    public static String firstUpperCase(String field) {
        if (!StringUtils.isEmpty(field)) {
            char[] cs = field.toCharArray();
            cs[0] -= 32;
            return String.valueOf(cs);
        } else {
            return field;
        }
    }


    private String tianTableName(String domainStr, CodeTable table) {
        return domainStr.replaceAll("<tableName>",table.getTableName());
    }

    private String tianPackage(String domainStr, String position) {
        String currentUrl = this.getClass().getPackage().toString();
        String packagePre = currentUrl.substring(0,currentUrl.lastIndexOf("."));
        //替换package
        String s = domainStr.replaceAll("<package>",packagePre+position);
        return s;
    }

}

@Data
class CodeTable{
    private String tableName;
    private String tableNameDesc;
}

@Data
class CodeCol{
    private String colName;
    private String colType;
    private String colDesc;
}
